#from ..libmoops import REG_NAMES
import depseudo

REG_NAMES = ['zero', 'at', 'v0', 'v1', 'a0', 'a1', 'a2', 'a3', 't0', 't1',
            't2', 't3', 't4', 't5', 't6', 't7', 's0', 's1', 's2', 's3', 's4',
            's5', 's6', 's7', 't8', 't9', 'k0', 'k1', 'gp', 'sp', 'fp', 'ra']

from re import compile as regex
import bitstring

# achtung! regex craziness ahoy!

immediates = {
    'hex':'(?:0[xX][0-9A-Fa-f]+)',
    'bin':'(?:0[bB][01]+)',
    'oct':'(?:0[Oo]?[0-7]+)',
    'dec':'(?:(?:0[^oOxXbB])?|[1-9]?[0-9]+)',
}

atoms = {
    'reg':r'\$(?:[0-2]?[0-9]|3[012]|%s)' % '|'.join(REG_NAMES),
    'imm':r'-?%(hex)s|%(bin)s|%(oct)s|%(dec)s' % immediates,
    'sep':r'[ \t]*?[,; \t][ \t]*',
    'lbl':r'\w+',
}

atoms['r_o'] = r'%(imm)s\(%(reg)s\)' % atoms

arg_types= {
    'R_type':regex(r'(%(reg)s)%(sep)s(%(reg)s)%(sep)s(%(reg)s);?\Z' % atoms),
    'I_type':regex(r'(%(reg)s)%(sep)s(%(reg)s)%(sep)s(%(imm)s);?\Z' % atoms),
    'J_type':regex(r'(%(lbl)s|%(imm)s);?\Z' % atoms),
    'LA':regex(r'(%(reg)s)%(sep)s(%(lbl)s);?\Z' % atoms),
    'coP_mv':regex(r'(%(reg)s);?\Z' % atoms),
    'FP_R':regex(r'(%(reg)s)%(sep)s(%(reg)s);?\Z' % atoms)
}

opcodes = {
    'add':'000000', 'addu':'000000', 'and':'000000', 'break':'000000',
    'div':'000000','divu':'000000', 'jalr':'000000', 'jr':'000000',
    'mfhi':'000000', 'mflo':'000000', 'mthi':'000000', 'mtlo':'000000',
    'mult':'000000', 'multu':'000000', 'nor':'000000', 'or':'000000',
    'sll':'000000', 'sllv':'000000', 'slt':'000000', 'sltu':'000000',
    'sra':'000000', 'srav':'000000', 'srl':'000000','srlv':'000000',
    'sub':'000000', 'subu':'000000', 'syscall':'000000', 'xor':'000000',
    'rfe':'000000', 'addi':'001000', 'addiu':'001001', 'andi':'001100',
    'beq':'000100', 'bgez':'000001', 'bgtz':'000111', 'blez':'000110',
    'bltz':'000001', 'bne':'000101', 'lb':'100000', 'lbu':'100100',
    'lh':'100001', 'lhu':'100101', 'lui':'001111', 'lw':'100011',
    'lwc1':'110001', 'ori':'001101', 'sb':'101000', 'slti':'001010',
    'sltiu':'001011', 'sh':'101001', 'sw':'101011', 'swc1':'111001',
    'xori':'001110',

    'j':'000010', 'jal':'000011',

    'add.s':'010001', 'cvt.s.w':'010001', 'cvt.w.s':'010001',
    'div.s':'010001', 'mfc0':'010000', 'mfc1':'010001', 'abs.s':'010001',
    'abs.d':'010001', 'neg.s':'010001', 'neg.d':'010001', 'mov.s':'010001',
    'mtc0':'010000', 'mtc1':'010001', 'mul.s':'010001', 'sub.s':'010001',
    'c.eq.s':'010001', 'c.eq.d':'010001', 'c.lt.s':'010001',
    'c.lt.d':'010001', 'c.le.s':'010001', 'c.le.d':'010001',

    'bc1t':'010001', 'bc1f':'010001', 'bc1tl':'010001', 'bc1fl':'010001'
}

functs = {
    'add':'100000', 'addu':'100001', 'and':'100100', 'break':'001101',
    'div':'011010', 'divu':'011011', 'jalr':'001001', 'jr':'001000',
    'mfhi':'010000', 'mflo':'010010', 'mthi':'010001', 'mtlo':'010011',
    'mult':'011000', 'multu':'011001', 'nor':'100111', 'or':'100101',
    'sll':'000000', 'sllv':'000100', 'slt':'101010', 'sltu':'101011',
    'sra':'000011', 'srav':'000111', 'srl':'000010', 'srlv':'000110',
    'sub':'100010', 'subu':'100011', 'syscall':'001100', 'xor':'100110',
    'rfe':'010000',

    'add.s':'000000', 'cvt.s.w':'100000', 'cvt.w.s':'100100',
    'div.s':'000011', 'mfc0':'000000', 'mfc1':'000000', 'abs.s':'000101',
    'abs.d':'000101', 'neg.s':'000111', 'neg.d':'000111', 'mov.s':'000110',
    'mtc0':'0000000', 'mtc1':'000000', 'mul.s':'000010', 'sub.s':'000001',
    'c.eq.s':'110010', 'c.eq.d':'110010', 'c.lt.s':'111100',
    'c.lt.d':'111100', 'c.le.s':'111110', 'c.le.d':'111110',
    'bc1t':'00001', 'bc1f':'00000', 'bc1tl':'00011', 'bc1fl':'00010'
}

forms = {
    'cvt.s.w':'10100', 'cvt.w.s':'10000', 'mfc0':'00000', 'mfc1':'00000',
    'mtc0':'00100', 'mtc1':'00100', 'bc1t':'01000', 'bc1f':'01000',
    'bc1tl':'01000', 'bc1fl':'01000'
}

def get_address(add):
    if add[0] in range(10): return bitstring.bits(add)
    assert 0

def get_immediate(imm, length=16):
    #print imm
    neg = imm[0] == '-'
    if neg:
        imm = imm[1:]
    if imm[0] == '0' and len(imm) > 1 and imm[1] not in 'xob':
        imm = '0o' + imm[1:]
    ret = int(imm, 0) * (-1 if neg else 1)
    return bitstring.Bits(int=ret, length=length)

def get_reg_num(reg):
    try: ret = REG_NAMES.index(reg[1:])
    except ValueError: ret = int(reg[1:])
    return bitstring.Bits(uint=ret, length=5)

def translate(instruction):
    translation = bitstring.BitString(32)

    instruction = instruction.strip()

    if ' ' not in instruction:
        return 0xc

    op, args = instruction.split(None,1)
    opcode = opcodes[op]
    #print opcode, args
    #args+=' '
    translation[:6] = bitstring.Bits(bin=opcode)

    if opcode == '000000':
        # R-type

        rs, rt, rd = arg_types['R_type'].match(args).groups()

        #print rs, rt, rd

        translation[6:11]  = get_reg_num(rs)
        translation[11:16] = get_reg_num(rt)
        translation[16:21] = get_reg_num(rd)

        translation[26:] = bitstring.Bits(bin=functs[op])

    elif opcode[:3] == '001':
        # I-type

        rs, rt, imm = arg_types['I_type'].match(args).groups()

        translation[6:11]  = get_reg_num(rs)
        translation[11:16] = get_reg_num(rt)
        translation[16:]   = get_immediate(imm, 16)

    elif opcode[:5] == '00001':
        # J-type

        imm = arg_types['J_type'].match(arg).groups()

        translation[6:] = get_address(imm)

    elif opcode[:4] == '0100':
        # FP-type
        assert 0

    return translation

def herpderp(fname):

    with open(fname) as f:
        for line in f:
            ins, _, _ = line.partition('#')
            ins = ins.strip()
            if ins and ins[0]!='.' and ins[-1]!=':':
                print ins,
                print translate(ins)
